距离上次更新已经 1534 天了,文章内容可能已经过时。

课程主页:

https://www.icourse163.org/course/NUDT-1003101005?tid=1460976445

这次回顾第12讲:语法分析——自下而上分析3。

第12讲LR(0)分析表的构造

LR分析法回顾

LR分析法

工作框架:

规范归约
  • 定义:假定α是文法G的一个句子,我们称序列αn,αn1,,α0α的一个规范归约,如果此序列满足:
    • αn=α
    • α0为文法的开始符号,即α0=S
    • 对任何i0inαi1是从αi经把句柄替换成为相应产生式左部符号而得到的
内容回顾
短语、直接短语和句柄
  • 定义:令G是一个文法,S是文法的开始符号,假定αβδ是文法G的一个句型,如果有

    SαAδA+β

    则称β是句型αβδ相对于非终结符A的短语。

  • 如果有Aβ,则称β是句型αβδ相对于规则Ab的直接短语。

  • 一个句型的最左直接短语称为该句型的句柄

  • 在一个句型对应的语法树中

    • 以某非终结符为根的两代以上的子树的所有末端结点从左到右排列就是相对于该非终结符的一个短语
    • 如果子树只有两代,则该短语就是直接短语
    • 最左两代子树末端就是句柄

规范句型
  • 规范归约是最左归约

  • 规范归约的逆过程就是最右推导

    SaAcBeaAcdeaAbcdeabbcde
  • 最右推导也称为规范推导

  • 由规范推导推出的句型称为规范句型

LR分析法

LR分析器的性质

  • 栈内的符号串和扫描剩下的输入符号串构成了一个规范句型
  • 一旦栈的顶部出现可归约串(句柄),则进行归约
测试:规范归约过程中栈内符号串

对于句子,在规范归约过程中,栈内的符号串和扫描剩下的输入符号串构成了一个规范句型,下面哪种格局不会出现:

答案是D。

活前缀

字的前缀、活前缀
  • 字的前缀:是指字的任意首部,如字abc的前缀有ε,a,ab,abc
  • 活前缀:是指规范句型的一个前缀,这种前缀不含句柄之后的任何符号。即,对于规范句型αβδβ为句柄,如果αβ=u1u2ur,则符号串u1u2ui(1ir)αβδ的活前缀。(δ必为终结符串)
  • 规范归约过程中,保证分析栈中总是活前缀,就说明分析采取的移进/归约动作是正确的
识别活前缀
  • 能否判断一个符号串是不是活前缀?
  • 对于一个文法G, 可以构造一个DFA,它能识别G的所有活前缀。

构造识别活前缀的DFA

文法的拓广
  • 将文法G(S)拓广为G(S)
    • 构造文法G,它包含了整个G,并引进不出现在G中的非终结符S、以及产生式SSSG的开始符号
    • GG的拓广文法
LR(0)项目
  • LR(0)项目
    • 在每个产生式的右部添加一个圆点
    • 表示我们在分析过程中看到了产生式多大部分
  • AXYZ有四个项目
    • AXYZ,AXYZ,AXYZ,AXYZ
  • Aα称为”归约项目”
  • 归约项目Sα称为”接受项目”
  • Aαaβ(aVT)称为”移进项目”
  • AαBβ(BVN)称为”待约项目”
示例

考虑文法G(S)

SEEaAbBAcAdBcBd

该文法的项目有:

构造识别文法所有活前缀的DFA
  • 构造识别文法所有活前缀的NFA
    • 若状态iXX1Xi1XiXn,状态jXX1Xi1XiXi+1Xn,则从状态i画一条标志为Xi的有向边到状态j
    • 若状态iXαAβA为非终结符,则从状态i画一条ε边到所有状态Aγ
  • 把识别文法所有活前缀的NFA确定化

识别活前缀的NFA

识别活前缀的DFA

将NFA确定化之后可得:

LR(0)项目集规范族

构成识别一个文法活前缀的DFA的项目集(状态)的全体称为文法的LR(0)项目集规范族。

通过计算项目集规范族构造识别活前缀的DFA

有效项目
  • 项目Aβ1β2对活前缀αβ1是有效的,其条件是存在规范推导SRαAωRαβ1β2ω
  • 在任何时候,分析栈中的活前缀X1X2Xm的有效项目集正是从识别活前缀的DFA的初态出发,读出X1X2Xm后到达的那个项目集(状态)。

有效项目的性质
  • 若项目AαBβ对活前缀η=δα是有效的且Bγ是一个产生式,则项目Bγη=δα也是有效的。

证明:

若项目AαBβ对活前缀η=δα是有效的,则有

SRδAωRδαBβω

βωRφω

那么

SRδAωRδαBβωRδαBφωRδαγφω

所以,Bγη=δα也是有效的

LR(0)项目集规范族的构造
  • 将文法G(S)拓广为G(S)
    • 构造文法G,它包含了整个G,并引进不出现在G中的非终结符S、以及产生式SSSG的开始符号
    • G唯一的“接受”态:仅含项目SS的状态

状态转换函数
  • 为了识别活前缀,我们定义一个状态转换函数GO是一个状态转换函数。I是一个项目集,X是一个文法符号。函数值GO(I,X)定义为:

    GO(I,X)CLOSURE(J)

    其中

    J={ 任何形如 AαXβ 的项目| AαXβ 属于I}
  • 直观上说,若I是对某个活前缀γ有效的项目集,那么,GO(I,X)便是对γX有效的项目集。

示例:项目集的转移函数计算

给定文法G(S)

SEEaAbBAcAdBcBd

考虑

I0={SE,EaA,EbB}

计算GO可得:

GO(I0,E)=closure(J)=closure({SE})={SE}=I1GO(I0,a)=closure(J)=closure({EaA})={EaA,AcA,Ad}=I2GO(I0,b)=closure(J)=closure({EbB})={EbB,BcB,Bd}=I3

LR(0)项目集规范族的构造算法
  • PROCEDURE ITEMSETS(G′)
  • BEGIN
    • C:={CLOSURE({S′→•S})} 
    • REPEAT
      • FOR C中每个项目集IG的每个符号X DO
        • IF GO(I, X)非空且不属于C THEN
          • GO(I, X)放入C族中;
    • UNTIL C不再增大
  • END

两种构造识别活前缀的DFA的方法

  1. 项目 → NFA → DFA
  2. Closure → GO → DFA

构造LR(0)分析表的算法

LR(0)分析表的构造
  • 假若一个文法G的拓广文法G的活前缀识别自动机中的每个状态(项目集)不存在下述情况

    • 既含移进项目又含归约项目;
    • 含有多个归约项目

    则称G是一个LR(0)文法。

构造LR(0)分析表的算法
  • 令每个项目集Ik的下标k作为分析器的状态,包含项目SS的集合Ik的下标k为分析器的初态。
  • 构造LR(0)分析表的ACTIONGOTO子表。
LR(0)分析表的ACTION和GOTO子表构造
  1. 若项目Aαaβ属于IkGO(Ik,a)Ija为终结符,则置ACTION[k,a]为“sj”。
  2. 若项目Aα属于Ik,那么,对任何终结符a(或结束符),置ACTION[k,a]为“rj”(假定产生式Aα是文法G的第j个产生式)。
  3. 若项目SS属于Ik,则置ACTION[k,]为“acc”。
  4. GO(Ik,A)IjA为非终结符,则置GOTO[k,A]=j
  5. 分析表中凡不能用规则1至4填入信息的空白格均置上“报错标志”。
示例:LR(0)分析表的构造

LR(0)分析示例

按照LR(0)分析表对bcd进行分析:

小结

  • 规范归约过程中,只要保证分析栈中总是活前缀,就说明分析采取的移进/归约动作是正确的
  • 哪些字符串是活前缀?能不能构造一个DFA来识别活前缀?
  • 项目->NFA ->DFA
  • Closure->GO->DFA
  • 将识别活前缀的DFA转换成LR分析表